package com.incar.chuxing.utils.ble;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.incar.chuxing.utils.ble.callback.IConnectCallback;
import com.incar.chuxing.utils.ble.callback.IConnectCallbackInner;
import com.incar.chuxing.utils.ble.callback.data.ICharacteristicCallback;
import com.incar.chuxing.utils.ble.callback.data.ICharacteristicCallbackInner;
import com.incar.chuxing.utils.ble.callback.scan.PeriodScanCallback;
import com.incar.chuxing.utils.ble.common.State;
import com.incar.chuxing.utils.ble.exception.BleException;
import com.incar.chuxing.utils.ble.manager.BluetoothConnectionController;
import com.incar.chuxing.utils.ble.model.BluetoothLeDevice;
import com.incar.chuxing.utils.ble.utils.Logger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import static com.incar.chuxing.utils.ble.common.ZXTBleConstant.DEFAULT_SCAN_TIME;
import static com.incar.chuxing.utils.ble.common.ZXTBleConstant.MSG_RECONNECT;


/**
 * @Description: Bluetooth操作类
 * @author: <a href="http://www.xiaoyaoyou1212.com">DAWI</a>
 * @date: 16/8/5 20:42.
 */
public class ViseBluetooth {

    private static final String TAG = ViseBluetooth.class.getSimpleName();
    private Context context;
    private BluetoothManager bluetoothManager;
    private BluetoothAdapter bluetoothAdapter;
    private IConnectCallback connectCallback;
    private IConnectCallbackInner connectCallbackInner;
    private ICharacteristicCallback receiveCallback;
    private ICharacteristicCallbackInner receiveCallbackInner;

    private State state = State.DISCONNECT;
    private int scanTimeout = DEFAULT_SCAN_TIME;

    private static ViseBluetooth viseBluetooth;

    private LinkedBlockingQueue<byte[]> dataInfoQueue = new LinkedBlockingQueue<>();

    private volatile int connectRetry = 3;//连接失败重新连接次数

    private volatile List<BluetoothConnectionController> bluetoothConnectionControllerList = new ArrayList<>();

    private BluetoothLeDevice bluetoothLeDevice;

    public static ViseBluetooth getInstance() {
        if (viseBluetooth == null) {
            synchronized (ViseBluetooth.class) {
                if (viseBluetooth == null) {
                    viseBluetooth = new ViseBluetooth();
                }
            }
        }
        return viseBluetooth;
    }

    private ViseBluetooth() {
    }

    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RECONNECT:
                    //重新连接
                    reConnect();
                    break;
            }
        }
    };

    @TargetApi(18)
    public void init(Context context) {
        if (Build.VERSION.SDK_INT >= 18) {
            if (this.context == null) {
                this.context = context.getApplicationContext();
                bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE);
                bluetoothAdapter = bluetoothManager.getAdapter();
                initConnectCallbackInner();
                initReceiverCallBackInner();
            }
        }
    }

    private void initReceiverCallBackInner() {

        receiveCallbackInner = new ICharacteristicCallbackInner() {
            @Override
            public void onSuccess(byte[] data) {
                receiveCallback.onSuccess(data);
            }

            @Override
            public void onFailure(BleException exception) {
                receiveCallback.onFailure(exception);
            }
        };
    }

    private void initConnectCallbackInner() {

        connectCallbackInner = new IConnectCallbackInner() {
            @Override
            public void onConnectSuccess(BluetoothGatt gatt, int status, int delayTime) {
                state = State.CONNECT_SUCCESS;
                connectCallback.onConnectSuccess(gatt, status, delayTime);
            }

            @Override
            public void onConnectFailure(BleException exception) {
                state = State.CONNECT_FAILURE;
                if (connectRetry > 0) {
                    handler.sendMessageDelayed(handler.obtainMessage(MSG_RECONNECT), 1000);
                } else {
                    connectCallback.onConnectFailure(exception);
                }
            }


            @Override
            public void onDisconnect(boolean isActive) {
                Logger.e(TAG, "--------isActive:" + isActive);
                if (isActive) {
                    state = State.DISCONNECT;
                    connectCallback.onDisconnect();
                } else {
                    //出现连上后自动断开异常
                    state = State.DISCONNECT;
                    if (connectRetry > 0) {
                        handler.sendMessageDelayed(handler.obtainMessage(MSG_RECONNECT), 1000);
                    } else {
                        Logger.e(TAG, "onDisconnect:" + isActive);
                        connectCallback.onConnectFailure(new BleException(null, null));
                    }
                }
            }

            @Override
            public void onConnectTimeout(BluetoothGatt bluetoothGatt) {
                state = State.CONNECT_TIMEOUT;
                if (connectRetry > 0) {
                    handler.sendMessageDelayed(handler.obtainMessage(MSG_RECONNECT), 1000);
                } else {
                    connectCallback.onConnectTimeout();
                }
            }

            @Override
            public void onRelease(BluetoothGatt bluetoothGatt) {
                removeBluetoothController(bluetoothGatt);
            }

            @Override
            public void onUnDiscoverServices(BluetoothGatt gatt) {
                state = State.CONNECT_TIMEOUT;
                if (connectRetry > 0) {
                    handler.sendMessageDelayed(handler.obtainMessage(MSG_RECONNECT), 1000);
                } else {
                    connectCallback.onConnectTimeout();
                }
            }
        };
    }

    private BluetoothConnectionController getBluetoothController(BluetoothGatt bluetoothGatt) {
        for (BluetoothConnectionController bluetoothConnectionController : bluetoothConnectionControllerList) {
            if (bluetoothConnectionController.getBluetoothGatt() == bluetoothGatt) {
                return bluetoothConnectionController;
            }
        }
        return null;
    }

    private void removeBluetoothController(BluetoothGatt bluetoothGatt) {

        Iterator<BluetoothConnectionController> iterator = bluetoothConnectionControllerList.iterator();
        while (iterator.hasNext()) {
            BluetoothConnectionController bluetoothConnectionController = iterator.next();
            if (bluetoothConnectionController.getBluetoothGatt() == bluetoothGatt) {
                Logger.e(TAG, "移除 BluetoothConnectionController");
                iterator.remove();
            }
        }
    }

    private void reConnect() {
        Logger.e(TAG, "开始  重新 连接");
        connect(bluetoothLeDevice, connectCallback, receiveCallback, true);
    }


    /*==================Android API 18 Scan========================*/
    @TargetApi(18)
    public void startLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        if (Build.VERSION.SDK_INT >= 18) {
            if (bluetoothAdapter != null) {
                bluetoothAdapter.startLeScan(leScanCallback);
                state = State.SCAN_PROCESS;
            }
        }
    }


    @TargetApi(18)
    public void stopLeScan(BluetoothAdapter.LeScanCallback leScanCallback) {
        if (Build.VERSION.SDK_INT >= 18) {
            if (bluetoothAdapter != null) {
                bluetoothAdapter.stopLeScan(leScanCallback);
            }
        }
    }

    public void startScan(PeriodScanCallback periodScanCallback) {
        if (periodScanCallback == null) {
            throw new IllegalArgumentException("this PeriodScanCallback is Null!");
        }
        periodScanCallback.setViseBluetooth(this).setScan(true).setScanTimeout(scanTimeout).scan();
    }

    public void stopScan(PeriodScanCallback periodScanCallback) {
        if (periodScanCallback == null) {
            throw new IllegalArgumentException("this PeriodScanCallback is Null!");
        }
        periodScanCallback.setViseBluetooth(this).setScan(false).removeHandlerMsg().scan();
    }


    /*==================connect========================*/

    @TargetApi(18)
    public synchronized void connect(BluetoothLeDevice bluetoothLeDevice, IConnectCallback connectCallback, ICharacteristicCallback receiveCallback) {
        connect(bluetoothLeDevice, connectCallback, receiveCallback, false);
    }

    @TargetApi(18)
    public synchronized void connect(BluetoothLeDevice bluetoothLeDevice, IConnectCallback connectCallback, ICharacteristicCallback receiveCallback, boolean isReconnect) {
        if (Build.VERSION.SDK_INT < 18) {
            throw new RuntimeException("The version number can not be less than 18!");
        }

        if (bluetoothLeDevice == null) {
            throw new IllegalArgumentException("this BluetoothLeDevice is Null!");
        }

        this.bluetoothLeDevice = bluetoothLeDevice;

        BluetoothDevice bluetoothDevice = bluetoothLeDevice.getDevice();

        if (bluetoothDevice == null || connectCallback == null || receiveCallback == null) {
            throw new IllegalArgumentException("this BluetoothDevice or IConnectCallback or receiveCallback is Null!");
        }

        if (!isReconnect) {
            connectRetry = 3;
        }

        this.connectCallback = connectCallback;
        this.receiveCallback = receiveCallback;


        if (connectRetry > 0) {
            connectRetry--;
            Logger.e(TAG, "当前第" + (3 - connectRetry) + "次连接");
        }

        int size = bluetoothConnectionControllerList.size();
        Logger.e(TAG, "当前连接个数：" + size);

        if (size == 6) {
            //设备繁忙，稍后再试
            return;
        }

        if (size > 0) {
            for (int i = 0; i < size; i++) {
                BluetoothConnectionController bluetoothConnectionController = bluetoothConnectionControllerList.get(i);
                bluetoothConnectionController.stopCallBack();
            }
        }

        BluetoothConnectionController bluetoothConnectionController = new BluetoothConnectionController(connectCallbackInner, receiveCallbackInner);
        bluetoothConnectionControllerList.add(bluetoothConnectionController);
        bluetoothConnectionController.connect(context, bluetoothDevice);
    }

    public synchronized void clear() {
        connectRetry = -1;
        Logger.e("clear :" + state);

        int size = bluetoothConnectionControllerList.size();

        if (size > 0) {
            BluetoothConnectionController bluetoothConnectionController = bluetoothConnectionControllerList.get(size - 1);
            bluetoothConnectionController.clear();
        }
    }


    @TargetApi(18)
    public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data, final ICharacteristicCallback bleCallback) {
        if (Build.VERSION.SDK_INT < 18) {
            return false;
        }

        int size = bluetoothConnectionControllerList.size();

        if (size > 0) {
            BluetoothConnectionController bluetoothConnectionController = bluetoothConnectionControllerList.get(size - 1);
            return bluetoothConnectionController.writeCharacteristic(characteristic, data, bleCallback);
        } else {
            return false;
        }
    }

    public boolean isConnected() {
        if (state == State.CONNECT_SUCCESS) {
            return true;
        } else {
            return false;
        }
    }


    /*==================get and set========================*/
    public BluetoothManager getBluetoothManager() {
        return bluetoothManager;
    }

    public BluetoothAdapter getBluetoothAdapter() {
        return bluetoothAdapter;
    }

    public ViseBluetooth setScanTimeout(int scanTimeout) {
        this.scanTimeout = scanTimeout;
        return this;
    }

    public void setState(State state) {
        this.state = state;
    }
}
